package textExcel;

// TODO: handle longer column names (ex: AA)
public class Spreadsheet
{
	// in spaces
	private static int CELL_WIDTH = 10;
	
    private Cell[][] cells;
    
    public Spreadsheet()
    {
        this(12, 20);
    }
    
    private Spreadsheet(int columns, int rows)
    {
        this.cells = new Cell[rows][columns];
        this.setEachCellToBlank();
    }
    
    private void setEachCellToBlank()
    {
        for(int r = 0; r < cells.length; r++)
            for(int c = 0; c < cells[0].length; c++)
                cells[r][c] = new Cell();
    }
    
    public boolean isValidCell(char column, int row)
    {
        int columnInt = (int) column - (int) 'A';
        
        return 0 <= columnInt && columnInt < cells[0].length &&
                1 <= row && row <= cells.length;
    }
    
    public boolean isValidCell(String s)
    {
    	try
    	{
	    	return s != null && s.length() == 2 &&
	    			this.isValidCell(s.charAt(0), Integer.parseInt(s.substring(1, 2)));
    	}
    	catch(NumberFormatException e)
    	{
    		return false;
    	}
    }
    
    public boolean isValidRange(String firstCell, String lastCell)
    {
    	if(!this.isValidCell(firstCell) || !this.isValidCell(lastCell))
    		return false;
    	
    	int firstColumn = (int) firstCell.charAt(0);
    	int lastColumn = (int) lastCell.charAt(0);
    	
    	int firstRow = Integer.parseInt(firstCell.substring(1));
    	int lastRow = Integer.parseInt(lastCell.substring(1));
    	
    	return lastColumn >= firstColumn && lastRow >= firstRow;
    }
    
    // returns true if the range is valid and each cell in the range contains a numeric value
    public boolean isValidNumericRange(String firstCell, String lastCell)
    {
    	if(! this.isValidRange(firstCell, lastCell))
    		return false;
    	
    	for(Cell[] row : this.getCellRange(firstCell, lastCell))
    		for(Cell cell : row)
    			if(!cell.hasNumericValue())
    				return false;
    	
    	return true;
    }
    
    /**
     * precondition: isValidRange(firstCell, lastCell)
     * @param firstCell
     * @param lastCell
     * @return
     */
    public Cell[][] getCellRange(String firstCell, String lastCell)
    {
    	if(!this.isValidRange(firstCell, lastCell))
    		throw new IllegalArgumentException("Invalid range");
    	
    	int firstColumn = (int) firstCell.charAt(0) - (int) 'A';
    	int lastColumn = (int) lastCell.charAt(0) - (int) 'A';
    	
    	int firstRow = Integer.parseInt(firstCell.substring(1)) - 1;
    	int lastRow = Integer.parseInt(lastCell.substring(1)) - 1;
    	
    	Cell[][] cellsInRange = new Cell[lastRow - firstRow + 1][lastColumn - firstColumn + 1];
    	
    	for(int column = 0; column < cellsInRange[0].length; column++)
    		for(int row = 0; row < cellsInRange.length; row++)
    			cellsInRange[row][column] = this.cells[row + firstRow][column + firstColumn];
    	
    	return cellsInRange;
    }
    
    /**
     * precondition: isValidCell(s)
     * @param s
     * @return
     */
    public Cell getCell(String s)
    {
    	if(!this.isValidCell(s))
            throw new IllegalArgumentException("Invalid cell reference");
    	
    	return this.getCell(s.charAt(0), Integer.parseInt(s.substring(1, 2)));
    }
    
    /**
     * 
     * precondition: isValidCell(column, row)
     * @param column
     * @param row
     * @return
     */
    public Cell getCell(char column, int row)
    {
        if(!this.isValidCell(column, row))
            throw new IllegalArgumentException("Invalid cell reference");
        
        return this.cells[row - 1][(int) column - (int) 'A'];
    }
    
    /**
     * precondition: isValidCell(column, row)
     * @param column
     * @param row
     * @param raw true for user input, false for computed value (ignored for non-formula cells)
     * @return
     */
    public String getCellValue(char column, int row, boolean raw)
    {
        Cell cell = getCell(column, row);
        
        if(cell.hasNumericValue() && !raw)
            return "" + cell.numericValue();
        
        return cell.toString();
    }
    
    /**
     * precondition: isValidCell(column, row) && cell != null
     * @param column
     * @param row
     * @param value
     */
    public void setCell(char column, int row, Cell cell)
    {
        if(!this.isValidCell(column, row))
            throw new IllegalArgumentException("Invalid cell reference");
        
        if(cell == null)
            throw new IllegalArgumentException("Invalid cell value");
        
        this.cells[row - 1][(int) column - (int) 'A'] = cell;
    }
    
    /**
     * precondition: isValidCell(cellReference) && cell != null
     * @param cellReference
     * @param cellValue
     */
    public void setCell(String cellReference, Cell cellValue)
    {
    	if(!this.isValidCell(cellReference))
            throw new IllegalArgumentException("Invalid cell reference");
        
        this.setCell(cellReference.charAt(0), Integer.parseInt(cellReference.substring(1, 2)), cellValue);
    }
    
    public void clear()
    {
        this.setEachCellToBlank();
    }
    
    /**
     * precondition: isValidCell(startColumn, startRow) && isValidCell(endColumn, endRow) && is valid range TODO: improve last part
     * @param startColumn
     * @param startRow
     * @param endColumn
     * @param ascending
     * @param endRow
     */
    public void sort(char startColumn, int startRow, char endColumn, int endRow, boolean ascending)
    {
        // TODO: errors
    }
    
    public String toString()
    {
    	String asString = "     |";
    	
    	// TODO: make work for longer column names
    	for(int x = 0; x < this.cells[0].length; x++)
    		asString += (char) ('A' + x) + "         |";
    	
    	asString += "\n";
    	
    	for(int row = 0; row < this.cells.length; row++)
    	{
    		asString += "" + (row + 1);
    		if(row + 1 < 10)
    			asString += " ";
    		asString += "   |";
    		
    		for(int column = 0; column < this.cells[0].length; column++)
    		{
    			String cellAsString = this.cells[row][column].toString();
    			if(this.cells[row][column].hasNumericValue())
    				cellAsString = "" + this.cells[row][column].numericValue();
    			
    			if(cellAsString.length() > CELL_WIDTH)
    				cellAsString = cellAsString.substring(0, CELL_WIDTH);
    			
    			asString += cellAsString;
    			
    			for(int x = 1; x <= CELL_WIDTH - cellAsString.length(); x++)
    				asString += " ";
    			
    			asString += "|";
    		}
    		
    		asString += "\n";
    	}
    	
    	return asString;
    }
}
